<?php

namespace App\Services\Ali;

use JohnLui\AliyunOSS;
use Exception;
use DateTime;
use OSS\Core\OssException;
use OSS\OssClient;

class OSS
{

    /**
     *  城市名称：
     *  经典网络下可选：杭州、上海、青岛、北京、张家口、深圳、香港、硅谷、弗吉尼亚、新加坡、悉尼、日本、法兰克福、迪拜
     *  VPC 网络下可选：杭州、上海、青岛、北京、张家口、深圳、硅谷、弗吉尼亚、新加坡、悉尼、日本、法兰克福、迪拜
     **/
    private $city = '';

    /** @var array 实例列表  */
    private $cityList = [
        'oss-cn-hangzhou' => '杭州',
        'oss-cn-shanghai' => '上海',
        'oss-cn-qingdao' => '青岛',
        'oss-cn-beijing' => '北京',
        'oss-cn-zhangjiakou' => '张家口',
        'oss-cn-shenzhen' => '深圳',
        'oss-cn-hongkong' => '香港',
        'oss-us-west-1' => '硅谷',
        'oss-us-east-1' => '弗吉尼亚',
        'oss-ap-southeast-1' => '新加坡',
        'oss-ap-southeast-2' => '悉尼',
        'oss-ap-northeast-1' => '日本',
        'oss-eu-central-1' => '法兰克福',
        'oss-me-east-1' => '迪拜',
    ];

    /** 经典网络 or VPC */
    private $networkType = '';

    private $AccessKeyId = '';
    private $AccessKeySecret = '';
    private $BucketName = '';

    private $ossClient;

    /**z`
     * 私有初始化 API，非 API，不用关注
     * @param boolean 是否使用内网
     * @throws Exception
     */
    public function __construct($isInternal = false)
    {
        $this->city = $this->cityList[config('alioss.ossCity')] ?? '';
        $this->networkType = config('alioss.ossNetworkIsVPC') ? 'VPC' : '经典网络';
        $this->AccessKeyId = config('alioss.AccessKeyId');
        $this->AccessKeySecret = config('alioss.AccessKeySecret');
        $this->BucketName = config('alioss.BucketName');

        if ($this->networkType == 'VPC' && !$isInternal) {
            throw new Exception("VPC 网络下不提供外网上传、下载等功能");
        }
        $this->ossClient = AliyunOSS::boot(
            $this->city,
            $this->networkType,
            $isInternal,
            $this->AccessKeyId,
            $this->AccessKeySecret
        );
    }

    /**
     * 使用外网上传文件 johnlui/aliyun-oss
     * @param $ossKey
     * @param $filePath
     * @param array $options
     * @return mixed
     * @throws Exception
     */
    public static function upload($ossKey, $filePath, $options = [])
    {
        $bucketName = self::getBaseBucketName();
        try {
            $oss = self::publicUpload($bucketName, $ossKey, $filePath, $options);
            $result = [
                'url' => self::getPublicObjectURL($bucketName, $ossKey),
                'info' => $oss,
            ];
        } catch (Exception $e) {
            // @todo think about how to retry.(static or queue?)
            throw new Exception($e->getMessage(), $e->getCode());
            //$result = ['url' => '', 'info' => $e->getMessage(),];
        }
        return $result;
    }

    /**
     * 使用 aliyun/aliyun-oss-php-sdk 上传文件
     * @param $object
     * @param $content
     * @return array
     * @throws Exception
     */
    public static function uploadBySDK($object, $content)
    {
        $accessKeyId = config('alioss.AccessKeyId');
        $accessKeySecret = config('alioss.AccessKeySecret');
        $endPoint = config('alioss.ossServer');
        $bucket = config('alioss.BucketName');
        try {
            $client = new OssClient($accessKeyId, $accessKeySecret, $endPoint);
            $oss = $client->putObject($bucket, $object, $content);
            $result = [
                'url' => $oss['oss-request-url'] ?? '',
                'info' => $oss,
            ];
        } catch (OssException $e) {
            throw new Exception($e->getMessage(), $e->getCode());
            //$result = ['url' => '', 'info' => $e->getMessage(),];
        }
        return $result;
    }

    /**
     * 获取 BucketName
     * @return \Illuminate\Config\Repository|mixed
     */
    public static function getBaseBucketName()
    {
        return config('alioss.BucketName');
    }

    /**
     * 使用外网上传文件
     * @param $bucketName
     * @param $ossKey
     * @param $filePath
     * @param array $options 参见 https://github.com/johnlui/AliyunOSS/blob/master/src/oss/src/Aliyun/OSS/OSSClient.php#L142-L148
     * @return \Aliyun\OSS\Models\PutObjectResult
     * @throws Exception
     */
    public static function publicUpload($bucketName, $ossKey, $filePath, $options = [])
    {
        $oss = new OSS();
        $oss->ossClient->setBucket($bucketName);
        return $oss->ossClient->uploadFile($ossKey, $filePath, $options);
    }

    /**
     * 使用阿里云内网上传文件
     * @param $bucketName
     * @param $ossKey
     * @param $filePath
     * @param array $options
     * @return \Aliyun\OSS\Models\PutObjectResult
     * @throws Exception
     */
    public static function privateUpload($bucketName, $ossKey, $filePath, $options = [])
    {
        $oss = new OSS(true);
        $oss->ossClient->setBucket($bucketName);
        return $oss->ossClient->uploadFile($ossKey, $filePath, $options);
    }


    /**
     * 使用外网直接上传变量内容
     * @param $bucketName
     * @param $ossKey
     * @param $content
     * @param array $options
     * @return \Aliyun\OSS\Models\PutObjectResult
     * @throws Exception
     */
    public static function publicUploadContent($bucketName, $ossKey, $content, $options = [])
    {
        $oss = new OSS();
        $oss->ossClient->setBucket($bucketName);
        return $oss->ossClient->uploadContent($ossKey, $content, $options);
    }

    /**
     * 使用阿里云内网直接上传变量内容
     * @param $bucketName
     * @param $ossKey
     * @param $content
     * @param array $options
     * @return \Aliyun\OSS\Models\PutObjectResult
     * @throws Exception
     */
    public static function privateUploadContent($bucketName, $ossKey, $content, $options = [])
    {
        $oss = new OSS(true);
        $oss->ossClient->setBucket($bucketName);
        return $oss->ossClient->uploadContent($ossKey, $content, $options);
    }


    /**
     * 使用外网删除文件
     * @param string bucket名称
     * @param string 目标 OSS object 名称
     * @return bool
     * @throws Exception
     */
    public static function publicDeleteObject($bucketName, $ossKey)
    {
        $oss = new OSS();
        $oss->ossClient->setBucket($bucketName);
        $oss->ossClient->deleteObject($bucketName, $ossKey);
        return true;
    }

    /**
     * 使用阿里云内网删除文件
     * @param string bucket名称
     * @param string 目标 OSS object 名称
     * @return bool
     * @throws Exception
     */
    public static function privateDeleteObject($bucketName, $ossKey)
    {
        $oss = new OSS(true);
        $oss->ossClient->setBucket($bucketName);
        $oss->ossClient->deleteObject($bucketName, $ossKey);
        return true;
    }


    /**
     * -------------------------------------------------
     *
     *
     *  下面不再分公网内网出 API，也不注释了，自行体会吧
     *
     *
     * -------------------------------------------------
     */

    public function copyObject($sourceBuckt, $sourceKey, $destBucket, $destKey)
    {
        $oss = new OSS();
        return $oss->ossClient->copyObject($sourceBuckt, $sourceKey, $destBucket, $destKey);
    }

    public function moveObject($sourceBuckt, $sourceKey, $destBucket, $destKey)
    {
        $oss = new OSS();
        return $oss->ossClient->moveObject($sourceBuckt, $sourceKey, $destBucket, $destKey);
    }

    /**
     * 获取公开文件的 URL
     * @param $bucketName
     * @param $ossKey
     * @return string
     * @throws Exception
     */
    public static function getPublicObjectURL($bucketName, $ossKey)
    {
        $oss = new OSS();
        $oss->ossClient->setBucket($bucketName);
        return $oss->ossClient->getPublicUrl($ossKey);
    }

    /**
     * 获取私有文件的URL，并设定过期时间，如 \DateTime('+1 day')
     * @param $bucketName
     * @param $ossKey
     * @param DateTime $expire_time
     * @return mixed
     * @throws Exception
     */
    public static function getPrivateObjectURLWithExpireTime($bucketName, $ossKey, DateTime $expire_time)
    {
        $oss = new OSS();
        $oss->ossClient->setBucket($bucketName);
        return $oss->ossClient->getUrl($ossKey, $expire_time);
    }

    public static function createBucket($bucketName)
    {
        $oss = new OSS();
        return $oss->ossClient->createBucket($bucketName);
    }

    public static function getAllObjectKey($bucketName)
    {
        $oss = new OSS();
        return $oss->ossClient->getAllObjectKey($bucketName);
    }

    public static function getObjectMeta($bucketName, $ossKey)
    {
        $oss = new OSS();
        return $oss->ossClient->getObjectMeta($bucketName, $ossKey);
    }

}
